﻿using jvm.classfile;
using jvm.classfile.attributeinfo;
using jvm.instructions;
using jvm.instructions.common;
using jvm.rtda;
using jvm.rtda.heap;
using jvm.rtda.heap.clazz;
using jvm.rtda.heap.pool;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace jvm
{
    class Interpreter
    {
        public static void Interpret(JThread thread, bool verboseInst)
        {
            try
            {
                Loop(thread, verboseInst);
            }
            catch (Exception)
            {
                LogFrames(thread);
            }
        }

        static void Loop(JThread thread, bool verboseInst)
        {
            ByteCodeReader reader = new ByteCodeReader();
            while (!thread.IsEmpty())
            {
                JFrame frame = thread.CurrentFrame();
                int pc = frame.nextPc;
                thread.pc = pc;

                reader.Reset(frame.method.code, pc);
                byte opcode = reader.ReadUint8();
                Instruction inst = InstructionsFactory.Create(opcode);
                inst.FetchOperands(reader);
                frame.nextPc = reader.pc;

                if (verboseInst)
                {
                    LogInstruction(frame, inst);
                }

                inst.Execute(frame);
            }
        }

        static void LogFrames(JThread thread)
        {
            while (!thread.IsEmpty())
            {
                JFrame curFrame = thread.PopFrame();
                JMethod curMethod = curFrame.method;
                string className = curMethod.jClass.name;
                int lineNumber = curMethod.GetLineNumber(curFrame.nextPc);
                Console.WriteLine(">> line:{0} pc:{1} {2}.{3}{4}", lineNumber, thread.pc, className, curMethod.name, curMethod.descriptor);
            }
        }

        static void LogInstruction(JFrame frame, Instruction instruction)
        {
            JMethod method = frame.method;
            Console.WriteLine("{0}.{1}() #{2} {3}", method.jClass.name, method.name, frame.nextPc, instruction);
        }
    }
}
